package com.corticon.dialogs.dao.hibernate;

import static org.hibernate.criterion.Restrictions.*;

import java.util.List;

import org.apache.commons.lang.StringUtils;

import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.MatchMode;
import org.hibernate.Criteria;

import org.springframework.stereotype.Repository;

import com.corticon.dialogs.dao.QuestionSetDAO;
import com.corticon.dialogs.model.QuestionSet;
import com.corticon.dialogs.model.UserGroup;
import com.corticon.dialogs.util.Pager;

@Repository("questionSetDAO")
public class QuestionSetHibernateDAO extends GenericHibernateDAO<QuestionSet, Long> implements
    QuestionSetDAO {

    @Override
    public List<QuestionSet> findByQuestion(String id, Pager pager) {

        Criteria c = this.getBaseCriteria().createAlias("QuestionInSet", "qqq").
            createCriteria("qqq.Question", "qq").add(eq("qq.questionId", Long.parseLong(id)));
        return c.list();
    }

    /**
     * Can we do this in a common way or with {@link Criteria}
     *
     * @return
     */
    @Override
    public String getMaxRefId() {
        return (String) getSession().createQuery("select max(qs.refId) from QuestionSet qs").
            uniqueResult();
    }

    @Override
    public QuestionSet findByRefIdAndVersion(String refId, long version) {
        return (QuestionSet) getSession().createQuery(
            "from QuestionSet qs where qs.refId = :refId and qs.version = :version").setParameter(
            "refId", refId).setParameter("version", version).uniqueResult();
    }

    @Override
    public long getMaxVersionByRefId(String refId) {
        return (Long) getSession().createQuery(
            "select max(qs.version) from QuestionSet qs where qs.refId = :refId").setParameter(
            "refId", refId).uniqueResult();
    }

    @Override
    public List<QuestionSet> getPageOfDataByGroup(UserGroup group, Pager pager) {
        return getPageOfData(getCriteriaByGroup(group), pager);
    }

    @Override
    public long getCountByGroup(UserGroup group) {
        return getCount(getCriteriaByGroup(group));
    }

    private Criteria getCriteriaByGroup(UserGroup group) {
        Criteria criteria = getBaseCriteria();
        criteria.createCriteria("modifiedBy").add(eq("userGroup", group));
        return criteria;
    }

    @Override
    public List<QuestionSet> findBySibling(QuestionSet questionSet) {
        Criteria criteria = getBaseCriteria();
        criteria.createCriteria("sibling").add(eq("questionSetId", questionSet.getQuestionSetId()));
        return criteria.list();
    }

    @Override
    public List<QuestionSet> search(String keywords, String availability, String status,
        String username, String groupName, Pager pager) {

        Criteria countCriteria = buildSearchCriteria(keywords, availability, status, username,
            groupName);
        pager.setNumOfRecords(getCount(countCriteria));

        Criteria criteria = buildSearchCriteria(keywords, availability, status, username, groupName);

        return getPageOfData(criteria, pager);
    }

    private Criteria buildSearchCriteria(String keywords, String availability, String status,
        String username, String groupName) {
        Criteria criteria = getBaseCriteria();

        keywords = keywords.trim();

        if (StringUtils.isNotEmpty(keywords)) {
            criteria.createAlias("sibling", "s", CriteriaSpecification.LEFT_JOIN).add(disjunction().
                add(ilike("refId", keywords, MatchMode.ANYWHERE)).add(ilike("displayTitle", keywords,
                MatchMode.ANYWHERE)).add(ilike("description", keywords, MatchMode.ANYWHERE)).add(ilike(
                "name", keywords, MatchMode.ANYWHERE)).add(ilike(
                        "vocabularyname", keywords, MatchMode.ANYWHERE)).add(ilike("s.refId", keywords,
                MatchMode.ANYWHERE)));
        }

        if (StringUtils.isNotEmpty(availability)) {
            criteria.createCriteria("availability").add(eq("code", availability));
        }

        if (StringUtils.isNotEmpty(status)) {
            criteria.createCriteria("status").add(eq("code", status));
        }

        if (StringUtils.isNotEmpty(username)) {
            criteria.createCriteria("modifiedBy").add(eq("username", username));
        } else if (StringUtils.isNotEmpty(groupName)) {
            criteria.createCriteria("modifiedBy").createCriteria("userGroup").add(eq("name",
                groupName));
        }

        return criteria;
    }
}